package com.golden.order02.service;

import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.parser.*;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;

import java.io.*;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.Iterator;
import java.util.Properties;

import com.golden.order02.domain.ResultDO;
import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.text.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import com.itextpdf.text.pdf.security.PrivateKeySignature;

@Service("digitallySigningPdfService")
public class DigitallySigningPdfServiceImpl implements IDigitallySigningPdfService {

    // 定义返回页码
    private int i = 0;

    private float[] resu = new float[3];

    private static Logger  logger = Logger.getLogger(DigitallySigningPdfServiceImpl.class);

  //  @Value("${pdf.fakeImgPath}")
    private  String fakeImgPath ;


    public  DigitallySigningPdfServiceImpl() {
    	
    	InputStream inStream = this.getClass().getClassLoader().getResourceAsStream("applicationOrder.properties");
    	Properties pro=new Properties();
    	try {
			pro.load(inStream);
			fakeImgPath=pro.getProperty("pdf.fakeImgPath");
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
    
    

    @Override
    public boolean sign(String signingPdf, String signedPdf, String pdfName,
                        String keyStorePath, String passWord, String sealPicPath) {
        return this.sign( signingPdf,  signedPdf,  null,
                 keyStorePath,  passWord,  sealPicPath, pdfName);
    }
        /**
         *
         * @param signingPdf 需要进行电子签章的pdf文件
         * @param signedPdf  签章之后的pdf文件
         * @param keyWord   pdf文件中的关键字，用于定位电子签章的位置。为了确保位置的唯一性，docx模板文件制作的时候要确保keyword的唯一性。
         * @param keyStorePath  pkcs#12证书路径
         * @param passWord   pkcs#12证书密码
         * @param sealPicPath  电子签章图片路径
         * @param pdfName  pdfdirectory中，替代图片的名字，必须是
         * @return
         */
    public boolean sign(String signingPdf, String signedPdf, String keyWord,
                        String keyStorePath, String passWord, String sealPicPath, String pdfName) {
        File signingPdfFile = new File(signingPdf);
        PdfReader reader = null;
        ByteArrayOutputStream signPDFData = null;
        PdfStamper stp = null;
        FileInputStream fos = null;
        float[]  result ;
        Image img = null;

/*
        if (pdfName != null) {
            try {
                manipulatePdf(signingPdf, signedPdf + "tmp", pdfName, sealPicPath);
                img = Image.getInstance(sealPicPath);
            } catch (Exception e) {
                logger.error(ResultSupport.SIGNING_PDF_NOT_EXIST_MSG + signingPdf);
                return false;
            }
        }
*/

        try {
            img = Image.getInstance(sealPicPath);

            result = getKeyWords(keyWord, img, signingPdf);

            float x = result[0];
            float y = result[1];
            int  pageNo = (int)result[2];

            BouncyCastleProvider provider = new BouncyCastleProvider();
            Security.addProvider(provider);
            KeyStore ks = KeyStore.getInstance("PKCS12", new BouncyCastleProvider());
            fos = new FileInputStream(keyStorePath);
            ks.load(fos, passWord.toCharArray()); // 私钥密码
            String alias = (String) ks.aliases().nextElement();
            PrivateKey key = (PrivateKey) ks.getKey(alias, passWord.toCharArray());
            Certificate[] chain = ks.getCertificateChain(alias);
            reader = new PdfReader(signingPdf);
            signPDFData = new ByteArrayOutputStream();
            // 临时pdf文件
            File temp = new File(signingPdfFile.getParent(), System.currentTimeMillis() + ".pdf");
            stp = PdfStamper.createSignature(reader, signPDFData, '\0', temp, true);

            PdfSignatureAppearance sap = stp.getSignatureAppearance();
            sap.setReason("数字签名，不可改变");
            sap.setLocation("");

            Image image = Image.getInstance(fakeImgPath);

            sap.setSignatureGraphic(image);

            sap.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);

            sap.setVisibleSignature(new Rectangle(x, y, x + 140, y + 140), pageNo,
                    "sr"+String.valueOf(System.nanoTime()));

            stp.getWriter().setCompressionLevel(PdfStream.BEST_COMPRESSION);
            ExternalDigest digest = new BouncyCastleDigest();
            ExternalSignature signature = new PrivateKeySignature(key, DigestAlgorithms.SHA512, provider.getName());
            MakeSignature.signDetached(sap, digest, signature, chain, null, null, null, 0, CryptoStandard.CADES);
            stp.close();
            reader.close();
            byte[] fileData =  signPDFData.toByteArray();

            FileOutputStream f = new FileOutputStream(new File(signedPdf));
            f.write(fileData);
            f.close();

            return true;
        } catch (Exception e) {
            logger.error(ResultDO.ORDER_ADD_ERROR, e);
        } finally {
            if (signPDFData != null) {
                try {
                    signPDFData.close();
                } catch (IOException e) {
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                }
            }
        }
        return false;
    }


    /**
     * 返回关键字或者图片所在的坐标和页数
     * float[0] >> X
     * float[1] >> Y
     * float[2] >> pageNo
     */
    private  float[] getKeyWords(String keyWord, final Image image, String filePath)
    {
        try
        {
            PdfReader pdfReader = new PdfReader(filePath);
            int pageNum = pdfReader.getNumberOfPages();
            PdfReaderContentParser pdfReaderContentParser = new PdfReaderContentParser(
                    pdfReader);

            pdfReaderContentParser.processContent(pageNum, new RenderListener()
            {

                public void renderText(TextRenderInfo textRenderInfo)
                {
                    if (keyWord == null) return;

                    String text = textRenderInfo.getText();
                    if (null != text && text.contains(keyWord))
                    {
                        Rectangle2D.Float   boundingRectange = textRenderInfo.getBaseline().getBoundingRectange();
                        resu[0] = boundingRectange.x;
                        resu[1] = boundingRectange.y;
                        resu[2] = pageNum;
                    }
                }

                @Override
                public void renderImage(ImageRenderInfo arg0)
                {
                    PdfImageObject image0;
                    try {
                        image0 = arg0.getImage();
                        byte[] imageByte = image0.getImageAsBytes();
                        Image imageInPDF = Image.getInstance(imageByte);
                        if(image0!=null && imageInPDF.equals(image)){
                            resu[0] = arg0.getStartPoint().get(0);
                            resu[1] = arg0.getStartPoint().get(1);
                            resu[2] = pageNum;
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (BadElementException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void endTextBlock()
                {
                    // TODO Auto-generated method stub

                }

                @Override
                public void beginTextBlock()
                {
                    // TODO Auto-generated method stub

                }
            });
        } catch (IOException e)
        {
            e.printStackTrace();
        }
        return resu;
    }

    private void manipulatePdf(String src, String dest, String pdfName, String imagePath) throws DocumentException, IOException {
        PdfReader reader = new PdfReader(src);
        PdfDictionary page = reader.getPageN(reader.getNumberOfPages());
        PdfDictionary resources = page.getAsDict(PdfName.RESOURCES);
        PdfDictionary xobjects = resources.getAsDict(PdfName.XOBJECT);
        PdfName imgRef = null;

        Iterator iter = xobjects.getKeys().iterator();
        while(iter.hasNext()){
            imgRef = (PdfName) iter.next();
            if (pdfName.equals(new String(imgRef.getBytes(),"UTF-8")))
                break;
        }

        PRStream stream = (PRStream) xobjects.getAsStream(imgRef);
        Image img =  Image.getInstance(imagePath);
        PdfImage image = new PdfImage(img, "", null);
        replaceStream(stream, image);

        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
        stamper.flush();
        stamper.close();
        reader.close();
    }

    private static void replaceStream(PRStream orig, PdfStream stream) throws IOException {
        orig.clear();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        stream.writeContent(baos);
        orig.setData(baos.toByteArray(), false);
        for (PdfName name : stream.getKeys()) {
            orig.put(name, stream.get(name));
        }
    }

/*
    public static void main(String[] args) throws Exception {

        DigitallySigningPdfServiceImpl digitallySigningPdf = new DigitallySigningPdfServiceImpl();

        digitallySigningPdf.sign("C:\\tmp\\pdf\\sign\\xxx.pdf", "C:\\tmp\\pdf\\sign\\signed2.pdf",
                "", "C:\\tmp\\pdf\\sign\\server.p12", "111111", "C:\\tmp\\pdf\\sign\\ettt.png", "/Im19"
        );

    }
*/
}
